﻿// Inner Fire 游戏引擎库
// 上传BUFFER类
//
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 这个类提供上传到默认缓冲区（或其它）的快捷操作。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-19

#pragma once

#include <dx/core/dx_core.h>
#include <dx/core/dx_utils.h>
#include <dx/core/dx_exception.h>

namespace ifire::dx {
/*
 * UploadBuffer类，用于方便上传
 */
template <typename T> class UploadBuffer {
public:
  UploadBuffer(ID3D12Device* device, //
      size_t element_count,            // 项目的数量
      bool is_constant_buffer)       // 是否是常量缓冲区
      : is_constant_buffer_(is_constant_buffer) {
    // 计算需要的内存数量
    element_count_ = element_count;
    element_byte_size_ = sizeof(T);
    if (is_constant_buffer_) { // 常量缓冲区必须是256B对齐
      element_byte_size_ = DxUtils::CalcConstantBufferSize(element_byte_size_);
    }
    data_byte_size_ = element_byte_size_ * element_count;

    // 创建上传缓冲区
    CD3DX12_HEAP_PROPERTIES upload_props(D3D12_HEAP_TYPE_UPLOAD);
    auto upload_buffer_desc = CD3DX12_RESOURCE_DESC::Buffer(data_byte_size_);
    HASSERT(device->CreateCommittedResource(&upload_props, D3D12_HEAP_FLAG_NONE,
        &upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
        IID_PPV_ARGS(&upload_buffer_)));

    // 对缓冲区进行印射
    HASSERT(upload_buffer_->Map(
        0, nullptr, reinterpret_cast<void**>(&mapped_data_)));

    // 只要还会修改当前的资源，我们就无须取消印射。
    // 但是，在资源被GPU使用期间，我们千万不能向该资源进行写操作（所以得使用同步技术）
  }

  UploadBuffer(const UploadBuffer& rhs) = delete;
  UploadBuffer& operator=(const UploadBuffer& rhs) = delete;

  ~UploadBuffer() {
    if (upload_buffer_ != nullptr) {
      upload_buffer_->Unmap(0, nullptr);
      upload_buffer_.Reset();
    }
    mapped_data_ = nullptr;
  }

  ID3D12Resource* Resource() const { return upload_buffer_.Get(); }
  void CopyData(size_t element_index, const T& data) {
    memcpy(&mapped_data_[element_index * element_byte_size_], &data, sizeof(T));
  }

  void CopyDataArray(size_t element_index, size_t count, const T* data_pointer) {
    memcpy(&mapped_data_[element_index * element_byte_size_], data_pointer,
        sizeof(T) * count);
  }
  void CopyAllAtOnce(size_t element_index, const void* src, size_t size){
    memcpy(&mapped_data_[element_index * element_byte_size_], src, size);
  }

  size_t size() const { return element_count_; }


private:
  ComPtr<ID3D12Resource> upload_buffer_;
  BYTE* mapped_data_;

  size_t element_byte_size_;
  size_t data_byte_size_;
  size_t element_count_;
  bool is_constant_buffer_;
};

template <typename T>
using UploadBufferPtr = std::unique_ptr<UploadBuffer<T>>;

} // namespace ifire